package com.kosmian.fastid;

import java.util.Calendar;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.LockSupport;

/**
 * 流水号生成器
 * <p>
 * 总长度（22位）：G + 17位时间戳 + 4位自增因子
 * <p>
 * 类雪花算法，适用于高并发环境。QPS实测值可达820万
 */
public final class GlobalNumberGenerator {
    private final static Generator GENERATOR = new Generator();

    public static String nextId() {
        return GENERATOR.nextId();
    }

    private final static class Generator {

        // ID前缀
        private static final String PREFIX = "G";

        // 序列号配置（13位=8192个/ms）
        private static final int SEQUENCE_BITS = 13;
        // 自增序列号最大值（8191）
        private static final long SEQUENCE_MASK = (1L << SEQUENCE_BITS) - 1;
        // 时间戳左移位
        private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS;

        // 时间戳格式工具
        private static final char[] DIGITS = "0123456789".toCharArray();
        private static final long[] DIVISORS = {1, 10, 100, 1_000, 10_000, 100_000, 1_000_000, 10_000_000, 100_000_000};

        // ID生成缓存器
        private static final int ID_LENGTH = PREFIX.length() + 17 + 4;
        private static final ThreadLocal<StringBuilder> ID_COMBINE = ThreadLocal.withInitial(
                () -> new StringBuilder(ID_LENGTH).append(PREFIX));

        // 时间戳缓存器
        private static final ThreadLocal<Calendar> CALENDAR = ThreadLocal.withInitial(
                Calendar::getInstance);

        // 合并状态：高51位=时间戳，低13位=序列号
        private final AtomicLong state = new AtomicLong(0L);

        public String nextId() {
            for (; ; ) {
                long oldState = state.get();
                long lastMs = oldState >>> TIMESTAMP_SHIFT;
                long currentMs = System.currentTimeMillis();

                // 时钟回拨处理
                if (currentMs < lastMs) {
                    handleClockBackward(lastMs, currentMs);
                    continue;
                }

                // 时间戳相同序列号自增
                long newMs, newSeq;
                if (currentMs == lastMs) {
                    newSeq = (oldState & SEQUENCE_MASK) + 1;
                    if (newSeq > SEQUENCE_MASK) {
                        newMs = waitNextMillis(lastMs);
                        newSeq = 1;
                    } else {
                        newMs = lastMs;
                    }
                } else {
                    newMs = currentMs;
                    newSeq = 1;
                }

                // 返回ID
                if (state.compareAndSet(oldState, (newMs << TIMESTAMP_SHIFT) | newSeq)) {
                    return formatId(newMs, newSeq);
                }

                // CAS失败时
                LockSupport.parkNanos(1_000);
            }
        }

        private String formatId(long currentTimestamp, long sequence) {
            StringBuilder sb = ID_COMBINE.get();
            sb.setLength(PREFIX.length());// 保留前缀

            // 时间戳部分（共19位）
            Calendar cal = CALENDAR.get();
            cal.setTimeInMillis(currentTimestamp);
            // 年（4位）
            sb.append(cal.get(Calendar.YEAR));
            // 月日时分秒（各2位）
            formatNumber(sb, cal.get(Calendar.MONTH) + 1, 2);
            formatNumber(sb, cal.get(Calendar.DAY_OF_MONTH), 2);
            formatNumber(sb, cal.get(Calendar.HOUR_OF_DAY), 2);
            formatNumber(sb, cal.get(Calendar.MINUTE), 2);
            formatNumber(sb, cal.get(Calendar.SECOND), 2);
            // 毫秒（3位）
            formatNumber(sb, cal.get(Calendar.MILLISECOND), 3);

            // 自增序列号（4位）
            formatNumber(sb, sequence, 4);
            return sb.toString();
        }

        private void formatNumber(StringBuilder sb, long number, int numberDigits) {
            for (int i = numberDigits - 1; i >= 0; i--) {
                sb.append(DIGITS[(int) ((number / DIVISORS[i]) % 10)]);
            }
        }

        private void handleClockBackward(long lastMs, long currentMs) {
            final long maxBackwardMs = 1_000;
            final long backwardMs = lastMs - currentMs;

            if (backwardMs > maxBackwardMs) {
                throw new IllegalStateException(
                        String.format("Clock moved backwards. Refusing to generate ID for %d ms", backwardMs));
            }

            long startWait = System.currentTimeMillis();

            while (true) {
                long nowSys = System.currentTimeMillis();

                if (nowSys >= lastMs) {
                    return;
                }

                long remaining = lastMs - nowSys;
                if (remaining > 0) {
                    LockSupport.parkNanos(Math.min(remaining, 1) * 1_000_000);
                }

                if (System.currentTimeMillis() - startWait > maxBackwardMs * 2) {
                    throw new IllegalStateException("Clock backward recovery timeout");
                }
            }
        }

        private long waitNextMillis(long lastTimestamp) {
            long timestamp = System.currentTimeMillis();
            int spins = 0;
            while (timestamp <= lastTimestamp) {
                if (++spins % 1_000 == 0) {
                    Thread.yield();
                }
                timestamp = System.currentTimeMillis();
            }
            return timestamp;
        }

    }
}
